﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Runtime.InteropServices;
using MPI;

namespace Dwarf.Managed
{
    /// <summary>
    /// User interface for solving the Structured Grid Problem.
    /// </summary>
    class Program
    {
        /// <summary>
        /// The settings for this dwarf.
        /// </summary>
        static private Settings settings;

        /// <summary>
        /// Get settings from the command line.
        /// </summary>
        /// <param name="args">
        /// Cli params.
        /// </param>
        /// <param name="sgsolver">
        /// Instance of Solver.
        /// </param>
        static void getSettings(String[] args, Solver sgsolver) 
        {
            bool error = false;
            if (Communicator.world.Rank == Settings.ROOT_PROCESS)
            {
                try
                {
                    settings = new Settings();
                    settings.Init(args, sgsolver);              //Parse all arguments for dwarf.
                } catch (Exception ex) {
                    Console.WriteLine(ex.Message);                    
                    error = true;                  
                }
            }

            //Distribute error if that occurred while parsing.
            MPI.Communicator.world.Broadcast(ref error, Settings.ROOT_PROCESS);
            if (error) throw new Exception("");

            //Distribute number of columns.
            MPI.Communicator.world.Broadcast(ref sgsolver.xmax, Settings.ROOT_PROCESS);
            //Distribute number of rows.
            MPI.Communicator.world.Broadcast(ref sgsolver.ymax, Settings.ROOT_PROCESS);
            //Distribute epsilon.
            MPI.Communicator.world.Broadcast(ref sgsolver.epsilon, Settings.ROOT_PROCESS);

            int count = (sgsolver.ymax - 2) / Communicator.world.Size;
            int i, j;

            if (Communicator.world.Rank == Settings.ROOT_PROCESS)
            {
                for (i = 1; i < Communicator.world.Size; i++)
                {
                    //Distribute values of nodes to their process
                    for (j = 0; j < count + 2; j++)
                        MPI.Communicator.world.Send(
                            sgsolver.grid[j + count * i + (sgsolver.ymax - 2) % Communicator.world.Size], 
                            i, 
                            i + j);
                }                
            }
            else
            {
                //Init arrays
                sgsolver.grid = new double[count + 2][];
                sgsolver.tempGrid = new double[count + 2][];
                
                for (i = 0; i < count + 2; i++)
                {
                    sgsolver.grid[i] = new double[sgsolver.xmax];
                    sgsolver.tempGrid[i] = new double[sgsolver.xmax];
                    for (j = 0; j < sgsolver.xmax; j++)
                    {
                        sgsolver.grid[i][j] = sgsolver.tempGrid[i][j] = 0;
                    }
                }

                //Receive values of nodes
                for (i = 0; i < count + 2; i++)
                {
                    Communicator.world.Receive(Settings.ROOT_PROCESS, Communicator.anyTag, ref sgsolver.grid[i]);
                    for (j = 0; j < sgsolver.xmax; j++)
                    {
                        sgsolver.tempGrid[i][j] = sgsolver.grid[i][j];
                    }
                }
            }
        }

        /// <summary>
        /// Point of the program start.
        /// </summary>
        /// <param name="args">
        /// Cli params.
        /// </param>
        static void Main(String[] args)
        {
            try
            {
                using (new MPI.Environment(ref args))                       // MPI Initialization.
                {
                    if (Communicator.world.Size == 1)
                    {
                        throw new Exception("Only one mpi process.");
                    }
                    

                    Solver sgsolver = new Solver();

                    getSettings(args, sgsolver);                            // Get settings from the command line.

                    Communicator.world.Barrier();
                    if (Communicator.world.Rank == Settings.ROOT_PROCESS)
                        settings.Start();                                   // Start new time count.
                    sgsolver.Solve();                                       // Solve the current problem.
                    Communicator.world.Barrier();
                    if (Communicator.world.Rank == Settings.ROOT_PROCESS)
                        settings.Finish(sgsolver);                          // Stop the time count and write results.
                }
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex.Message);
                //usage();
            }
        }
    }
}
